package main

import (
	"flag"
	"log"
	"fmt"
	"rand"
	"runtime/pprof"
	"os"
	"sync"
)

var (
	xSize      = flag.Int("xsize", 10, "X size.")
	ySize      = flag.Int("ysize", 13, "Y size.")
	iterations = flag.Int("iterations", 100, "Number of simulations.")
	jobs       = flag.Int("jobs", 3, "Number of jobs.")
	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
)

type pos struct {
	X, Y int
}

func (p pos) Equals(q pos) bool {
	return p.X == q.X &&
		p.Y == q.Y
}

func (in pos) Add(delta pos) (out pos) {
	out = in
	out.X += delta.X
	out.Y += delta.Y
	return
}

type board struct {
	XSize, YSize int
	Crt          pos
	End          pos
	directions   [4]pos
}

func NewBoard() *board {
	b := new(board)
	b.directions[0] = pos{X: 1, Y: 0}
	b.directions[1] = pos{X: -1, Y: 0}
	b.directions[2] = pos{X: 0, Y: -1}
	b.directions[3] = pos{X: 0, Y: 1}
	return b
}

func (b *board) inside(p pos) bool {
	return !(p.X < 0 || p.Y < 0 ||
		p.X > b.XSize-1 || p.Y > b.YSize-1)
}

func (b *board) move() (done bool) {
	options := make([]int, 4)
	optionCount := 0
	for i := 0; i < 4; i++ {
		if b.inside(b.Crt.Add(b.directions[i])) {
			options[optionCount] = i
			optionCount++
		}
	}
	r := rand.Int() % optionCount

	b.Crt = b.Crt.Add(b.directions[options[r]])
	return b.Crt.Equals(b.End)
}


func main() {
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		if err = pprof.StartCPUProfile(f); err != nil {
			log.Fatal(err)
		}
		defer pprof.StopCPUProfile()
	}

	iteration := make(chan int)
	go func() {
		for i := 0; i < *iterations; i++ {
			iteration <- i
		}
		close(iteration)
	}()

	wg := new(sync.WaitGroup)
	for i := 0; i < *jobs; i++ {
		wg.Add(1)
		fmt.Printf("Starting job %d.\n", i)
		go func() {
			for {
				it, ok := <-iteration
				if !ok {
					break
				}
				fmt.Printf("Starting iteration %d.\n", it)
				b := NewBoard()
				b.XSize = *xSize
				b.YSize = *ySize
				b.Crt = pos{X: 0, Y: 0}
				b.End = pos{X: *xSize-1, Y: *ySize-1}
				
				moves := 0
				for {
					done := b.move()
					if done {
						break
					}
					moves++
				}
				fmt.Printf("Thread done after %d moves.\n", moves)
			}
			wg.Done()
		}()
	}
	wg.Wait()
}
